package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.domain.ProductSku;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.query.SkuQuery;
import com.spzx.product.service.ProductSkuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.product.api.domain.ProductSkuVo;
import com.spzx.product.api.domain.SkuPriceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品sku 服务实现类
 * </p>
 *
 * @author lynn
 * @since 2025-03-12
 */
@Service
@Slf4j
public class ProductSkuServiceImpl extends ServiceImpl<ProductSkuMapper, ProductSku> implements ProductSkuService {

    @Autowired
    private ProductSkuMapper productSkuMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public List<ProductSkuVo> getTopSaleList() {
        return productSkuMapper.getTopSaleList();
    }

    @Override
    public List<ProductSkuVo> selectProductSkuList(SkuQuery skuQuery) {
        List<ProductSkuVo> list = productSkuMapper.selectProductSkuList(skuQuery);
        return list;
    }

    @Override
    public Map<String, Long> getSkuSpecValue(Long id) {
        // 查询一个商品的所有sku
        LambdaQueryWrapper<ProductSku> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(ProductSku::getProductId, id)
                .select(ProductSku::getId, ProductSku::getSkuSpec);
        List<ProductSku> productSkuList = baseMapper.selectList(queryWrapper);
        // 封装数据
        Map<String, Long> skuSpecValueMap = new HashMap<>();
        productSkuList.forEach(item -> {
            skuSpecValueMap.put(item.getSkuSpec(), item.getId());
        });
        return skuSpecValueMap;
    }

    @Override
    public SkuPriceVo getSkuPrice(Long skuId) {

        ProductSku productSku = productSkuMapper.selectOne(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getId, skuId).select(ProductSku::getSalePrice, ProductSku::getMarketPrice));
        SkuPriceVo skuPriceVo = new SkuPriceVo();
        BeanUtils.copyProperties(productSku, skuPriceVo);
        return skuPriceVo;
    }

    @Override
    public ProductSku getProductSkuById(Long skuId) {

        //1.从redis中获取sku信息 如果有直接返回
        String dataKey = "product:sku:" + skuId;
        ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
        if (productSku != null) {
            log.info("命中缓存：{}", dataKey);
            return productSku;
        }
        //2.如果没有则查询数据库，加分布式锁
        String lockKey = "product:sku:lock:" + skuId;
        String lockValue = UUID.randomUUID().toString().replaceAll("-", "");

        log.info("加分布式锁...");
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS);

        //拿到锁
        if (flag) {
            try {
                log.info("拿到分布式锁...");
                //3.再次从redis中获取信息，如果有直接返回
                //这一步重试是为了防止该线程在等待拿锁的过程中，先拿到锁的线程已经从数据库中查询数据存入redis 避免重复查询数据库
                productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
                if (productSku != null) {
                    log.info("命中缓存：{}", dataKey);
                    return productSku;
                }

                //4.缓存中没有，查询数据库
                log.info("查询数据库");
                productSku = productSkuMapper.selectById(skuId);
                long ttl = productSku == null ? 1 : 10; //如果查不到数据，则设置缓存时间为1分钟,防止缓存穿透
                if (productSku == null) { //防止再次查出数据为空
                    log.info("数据库中不存在该id对应数据");
                    productSku = new ProductSku();
                }

                //5.将查到的数据放入redis缓存 (无论是数据库中不存在该id对应的数据或者是存在 都需要存入redis)
                redisTemplate.opsForValue().set(dataKey, productSku, ttl, TimeUnit.MINUTES);
                return productSku;
            } catch (Exception e) {
                log.error("查询商品sku信息异常：{}", e.getMessage());
                throw new RuntimeException(e);
            } finally {
                //6.利用lua脚本释放分布式锁
                //锁的释放
                String script = """
                        if redis.call('get', KEYS[1]) == ARGV[1]
                            then return redis.call('del', KEYS[1])
                        else
                            return 0
                        end
                        """;
                Boolean result = (Boolean) redisTemplate.execute(
                        new DefaultRedisScript<>(script, Boolean.class),
                        Arrays.asList(lockKey),
                        lockValue);
                System.out.println("result = " + result);
            }
        } else { //没拿到锁，进行重试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return getProductSkuById(skuId);
        }

    }

    /**
     * 根据skuIdList查询sku价格
     * @param skuIdList
     * @return
     */
    @Override
    public List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList) {
        if (skuIdList == null || skuIdList.isEmpty()) {
            throw new ServiceException("skuIdList不能为空");
        }

        return productSkuMapper.selectBatchIds(skuIdList).stream().map(item -> {
            SkuPriceVo skuPriceVo = new SkuPriceVo();
            BeanUtils.copyProperties(item, skuPriceVo);
            skuPriceVo.setSkuId(item.getId());
            return skuPriceVo;
        }).collect(Collectors.toList());

    }

}
